package com.luxsoft.siipap.migracion;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;

import org.apache.log4j.Logger;
import org.hibernate.SessionFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.util.Assert;


/**
 * 
 * @author Ruben Cancino
 * 
 * TODO Implementar un PropertyEditor para Cantidad Monetaria y otros tipos clases
 *
 */
public class DefaultMapper implements RowMapper,InitializingBean,ApplicationContextAware{
	
	private ApplicationContext applicationContext;	
	private HibernateTemplate  hibernateTemlate;
	protected Logger logger=Logger.getLogger(getClass());
	private Class beanClass;
	private BeanWrapperImpl wrapper;
	private Map<String,String> propertyColumnMap;
	
	public DefaultMapper(){		
		
	}

	/**
	 * Realiza el mapeo utilizando de la siguiente manera
	 * iterando sobre los Map.Entry correspondientes al  propertyColumnMap suminstrado:
	 * 	- Utiliza el valor de la llave para definir la propiedad del bean
	 *  - Utiliza el valor del entry como el nombre de la columna del ResultSet y busca su valor con getObject
	 *  - Con el valor de la columna lo intenta asignar  a la poriedad definida en el key  
	 *  En cualquier error esta implementacion regresa nulo si existe algun
	 *  tipo de error	  
	 */
	public Object mapRow(ResultSet rs, int rowNum) throws SQLException {		
		wrapper.setWrappedInstance(BeanUtils.instantiateClass(getBeanClass()));
		try {
			for(Map.Entry<String, String> entry:getPropertyColumnMap().entrySet()){
				final String property=entry.getKey();			
				final String column=entry.getValue();
				Object val=rs.getObject(column);				
				setValue(property,convertColumnValue(column, val));
			}	
					
		} catch (SQLException sqe) {
			logger.error("Error al accesar el ResultSet: "+sqe.getMessage(),sqe);
		}catch (BeansException be) {
			logger.error("Error asignando valor al bean",be);
			return null;
		}		
		return wrapper.getWrappedInstance();		
	}
	
	/**
	 * Asigna el valor a la propiedad, Permite a las subclases personalizar
	 * la tarea 
	 * 
	 * @param property
	 * @param val
	 */
	protected void setValue(String property,Object val){
		wrapper.setPropertyValue(property,val);
	}
	
	/**
	 * Template method para dar oportunidad a convertir el valor de una columna
	 * en algun otro adecuado para el bean. Util para cuando la conversion
	 * no es facilmente implementable desde un PropertyEditor para la propiedad
	 * en curso ej String SI to boolean
	 * La implementacion por defecto regresa el valor 
	 * 
	 * @param col
	 * @param val
	 * @return
	 */
	protected Object convertColumnValue(String col,Object val){
		return val;
	}
	
	protected BeanWrapperImpl getWrapper(){
		return wrapper;
	}
	
	protected BeanWrapperImpl createWrapper(){
		return new BeanWrapperImpl(getBeanClass());
	}
	
	public Class getBeanClass() {
		return beanClass;
	}
	public void setBeanClass(Class beanClass) {
		this.beanClass = beanClass;
	}	

	/**
	 * Maepo de nombre de propiedades con nombre de columnas
	 * 
	 * @return
	 */
	public Map<String, String> getPropertyColumnMap() {
		return propertyColumnMap;
	}
	public void setPropertyColumnMap(Map<String, String> propertyColumnMap) {
		this.propertyColumnMap = propertyColumnMap;
	}

	/**
	 * Valida que el Mapper este correctamente inicialzado y que la instancia
	 * e inicializa la instancia de {@link BeanWrapperImpl} 
	 */
	public void afterPropertiesSet() throws Exception {
		Assert.notNull(getPropertyColumnMap(),"Needs a Map of Properties - Columns");
		Assert.notEmpty(getPropertyColumnMap().values(),"No hay propiedades asignables");
		wrapper=createWrapper();
		
	}

	/*
	 * Esta clase utiliza el AplicationContext para localizar instancias de
	 * PropertyEditor
	 * 
	 * (non-Javadoc)
	 * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
	 */
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext=applicationContext;
		
	}

	public HibernateTemplate getHibernateTemlate() {
		return hibernateTemlate;
	}

	public void setHibernateTemlate(HibernateTemplate hibernateTemlate) {
		this.hibernateTemlate = hibernateTemlate;
	}
	
	
}
